# 面向对象编程
# 类和对象
# 类是对象抽象，对象是类的具体实现
# 三大特性：封装、继承、多态

# 定义
# 封装：类里面可以封装 属性、方法
# 1、代码上的封装
# 2、安全性的封装：在Python没有严格意义上的封装，更过是靠自觉
class Person():
    # 定义属性
    # 构造方法 类的属性需要定义在构造方法中
    # 类中的所有方法第一个参数都是self，在调用的时候会自动传入
    # self就是对象本身 相当于Java中的this
    def __init__(self, id, name, age, gender):
        # 定义即赋值
        self.id = id
        self.name = name
        self.age = age
        self.gender = gender
        self.common = "common"
        # 如果某些属性不想公开 则可以在属性之前加上 __
        self.__secret = "这是一个秘密"

    # 定义方法
    def run(self):
        print("%s run run run" % (self.name))

    # 相当于Java Object 中的 tostring
    def __str__(self):
        return "class Person(%s,%s,%d,%s)" % (self.id, self.name, self.age, self.gender)


# 实例化
# 构造对象
person = Person("100001", "张三", 18, "男")
print(person)
print(person.id)
print(person.name)
person.run()


# 继承
class Teacher(Person):
    def __init__(self, id, name, age, gender, subject):
        super(Teacher, self).__init__(id, name, age, gender)
        self.subject = subject

    def teach(self):
        print("%s 会上 %s 课" % (self.name, self.subject))

    # 定义方法
    def run(self):
        print("Teacher %s run run run" % (self.name))

    # 重写父类的方法
    def __str__(self):
        return "class Teacher(%s,%s,%d,%s,%s)" % (self.id, self.name, self.age, self.gender, self.subject)

    def __test(self):
        print("测试封装方法")


teacher = Teacher("100002", "李老师", 29, "男", "体育")
print(teacher)
print(teacher.id)
print(teacher.name)
print(teacher.subject)
teacher.run()
teacher.teach()


# 多态
# 父类引用指向子类对象
# 只要它长得像鸭子那就是鸭子
# 在Python没有严格的类型检查机制
def runTwice(c):
    c.run()
    c.run()


runTwice(person)
runTwice(teacher)

# print(person.__secret) # 会报错 因为 __secret属性被 “封装” 了
print(person._Person__secret)  # 还是可以通过 _类名+属性名 获取
# 方法同样可以 “封装”
teacher._Teacher__test()
